/*
 * GLFont.cpp
 *
 *  Created on: 01-12-2010
 *      Author: Przemek
 */

#include "gui/GLFont.hpp"
using namespace std;

GLFont::~GLFont()
{
	freeResources();
}

void GLFont::load(const string& filename, GLuint textureId)
{
	freeResources();

	FILE* inputFile = fopen(filename.c_str(), "rb");
	if(inputFile == NULL)
	{
		throw LoadFontException("Cannot open font file:" + filename);
	}

	loadFontData(inputFile);
	font.textureId = textureId;
	char* textureData = loadTextureData(inputFile);
	createOglTextureFromData(textureData);
	calculateCharsSizes();

	delete[] textureData;
	fclose(inputFile);
}

void GLFont::begin() const
{
	glEnable( GL_BLEND);
	glEnable( GL_TEXTURE_2D);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glBindTexture(GL_TEXTURE_2D, font.textureId);
}

void GLFont::drawTextAt(const string& text, const Vector& position) const
{
	const int length = text.length();

	glBegin( GL_QUADS);

	int x = static_cast<int> (position.getX());
	int y = static_cast<int> (position.getY());
	for(int i = 0; i < length; ++i)
	{
		const FontChar& character = getFontChar(text[i]);

		glTexCoord2f(character.tx1, character.ty1);
		glVertex2i(x, y);
		glTexCoord2f(character.tx1, character.ty2);
		glVertex2i(x, y + character.height);
		glTexCoord2f(character.tx2, character.ty2);
		glVertex2i(x + character.width, y + character.height);
		glTexCoord2f(character.tx2, character.ty1);
		glVertex2i(x + character.width, y);

		x += character.width;
	}

	glEnd();
}

void GLFont::end() const
{
	glDisable( GL_TEXTURE_2D);
	glDisable( GL_BLEND);
}

Dimension GLFont::measureTextSize(const string& text) const
{
	int width = 0;
	int maximumHeight = 0;

	const int length = text.length();
	for(int i = 0; i < length; ++i)
	{
		const FontChar& character = getFontChar(text[i]);

		width += character.width;
		const int newMaximumHeightCandidate = character.height;
		if(newMaximumHeightCandidate > maximumHeight)
		{
			maximumHeight = newMaximumHeightCandidate;
		}
	}

	return Dimension(width, maximumHeight);
}

void GLFont::freeResources()
{
	delete[] font.chars;
	font.chars = NULL;
}

void GLFont::loadFontData(FILE* inputFile)
{
	fread(&font, sizeof(Font), 1, inputFile);

	const int charactersNumber = font.charsEndIndex - font.charsStartIndex + 1;
	font.chars = new FontChar[charactersNumber];

	fread(font.chars, sizeof(FontChar), charactersNumber, inputFile);
}

char* GLFont::loadTextureData(FILE* inputFile)
{
	const int textureSize = font.textureWidth * font.textureHeight * 2;

	char *textureBytes = new char[textureSize];
	fread(textureBytes, sizeof(char), textureSize, inputFile);
	return textureBytes;
}

void GLFont::createOglTextureFromData(char* textureData)
{
	glBindTexture(GL_TEXTURE_2D, font.textureId);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, font.textureWidth, font.textureHeight, 0,
			GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, reinterpret_cast<const GLvoid *> (textureData));
}

void GLFont::calculateCharsSizes()
{
	const int charsNumber = font.charsEndIndex - font.charsStartIndex + 1;
	for(int i = 0; i < charsNumber; ++i)
	{
		const float widthDifference = (font.chars[i].tx2 - font.chars[i].tx1);
		const float heightDifference = (font.chars[i].ty2 - font.chars[i].ty1);
		font.chars[i].width = static_cast<int> (widthDifference * font.textureWidth);
		font.chars[i].height = static_cast<int> (heightDifference * font.textureHeight);
	}
}

const GLFont::FontChar& GLFont::getFontChar(const char c) const
{
	return font.chars[static_cast<int> (c - font.charsStartIndex)];
}
